package com.sparkseries.service.impl;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.sparkseries.common.security.domain.SecurityUser;
import com.sparkseries.common.util.domain.Result;
import com.sparkseries.common.util.exception.BusinessException;
import com.sparkseries.common.util.tool.ObjectConvertTool;
import com.sparkseries.dao.SysNoticeMapper;
import com.sparkseries.dao.SysRoleMapper;
import com.sparkseries.dao.SysUserMapper;
import com.sparkseries.domain.MyNotice;
import com.sparkseries.domain.SysNotice;
import com.sparkseries.domain.SysUser;
import com.sparkseries.domain.vo.SysNoticeVO;
import com.sparkseries.dto.SysNoticeDTO;
import com.sparkseries.enums.DefaultStatusEnum;
import com.sparkseries.enums.NoticeStatusEnum;
import com.sparkseries.common.util.redis.RedisKeys;
import com.sparkseries.common.util.redis.RedisUtils;
import com.sparkseries.redis.Producer;
import com.sparkseries.service.SysNoticeService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * 通知管理
 * TODO 有附件的情况，文件管理 审核的通过和驳回情况只适用于单独发送的消息
 */
@Slf4j
@Service
public class SysNoticeServiceImpl implements SysNoticeService {
    @Resource
    private SysNoticeMapper sysNoticeMapper;
    @Resource
    private SysRoleMapper sysRoleMapper;
    @Resource
    private SysUserMapper sysUserMapper;
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private Producer producer;

    /**
     * 推送通知
     *
     * @param sysNoticeDTO 通知传输类
     * @return 默认响应类
     */
    @Override
    public Result<?> createNotice(SysNoticeDTO sysNoticeDTO) {
        SysNotice sysNotice = convertToSysNotice(sysNoticeDTO);

        Long noticeId = sysNotice.getId();
        //当发送保存的草稿时
        if (ObjectUtils.isNotNull(noticeId)) {
            deleteNoticeCache(noticeId);
            //设置发送时间
            sysNoticeDTO.setSendDate(new Date());
            updateNotice(sysNoticeDTO);
        } else {
            // 使用IdWorker工具类生成一个随机的id存入通知类
            noticeId = IdWorker.getId();
            sysNotice.setId(noticeId);
            // 设置发送者
            /*SecurityUser currentUser = getCurrentUser();
            sysNotice.setCreateBy(currentUser.getUsername());
            sysNotice.setCreateId(currentUser.getId());*/
            //判断是否存为草稿
            if (sysNotice.getStatus() != 2) {
                // 设置发送时间
                sysNotice.setSendDate(new Date());
            }

            try {
                if (producer.createNotice(sysNotice).get() <= 0) {
                    log.error("新增通知失败，通知信息:{}", sysNotice);
                    throw new BusinessException("新增通知时发生异常");
                }
            } catch (InterruptedException | ExecutionException e) {
                throw new RuntimeException(e);
            }

            // 存入缓存
            redisUtils.set(getNoticeKey(noticeId), sysNotice);
        }

        // 发送通知
        NoticeToUsers(sysNotice, 1);

        log.info("新增推送通知成功，通知ID:{}", sysNotice.getId());
        return Result.ok("新增推送通知成功");
    }

    /**
     * 撤回推送通知
     *
     * @param noticeId 通知id
     * @return 默认响应类
     */
    @Override
    public Result<?> revokeNotice(Long noticeId) {
        deleteNoticeCache(noticeId);

        SysNotice sysNotice = sysNoticeMapper.getNoticeById(noticeId);

        //撤回推送通知只能撤回5分钟内发送的通知
        if (isFiveMinutesPassed(sysNotice.getCreateDate())) {
            log.error("撤回推送通知失败，只能撤回5分钟内的通知");
            throw new BusinessException("撤回推送通知失败，只能撤回5分钟内的通知");
        }
        if (sysNoticeMapper.revokeNotice(noticeId) <= 0) {
            log.error("撤回推送通知失败，通知ID:{}", noticeId);
            throw new BusinessException("撤回推送通知时发生异常");
        }

        NoticeToUsers(sysNotice, 0);

        log.info("撤回推送通知成功，通知ID:{}", noticeId);
        return Result.ok("撤回推送通知成功");
    }

    /**
     * 删除推送通知
     * 逻辑删除，删除的通知会在七天后自动删除
     *
     * @param noticeId 通知id
     * @return 默认响应类
     */
    @Override
    public Result<?> deleteNotice(Long noticeId) {
        deleteNoticeCache(noticeId);

        if (sysNoticeMapper.updateNoticeStatus(noticeId, NoticeStatusEnum.DELETE.getCode()) <= 0) {
            log.error("删除推送通知失败，通知ID:{}", noticeId);
            throw new BusinessException("删除推送通知时发生异常");
        }

        log.info("删除推送通知成功，通知ID:{}", noticeId);
        return Result.ok("删除推送通知成功");
    }

    /**
     * 删除我的通知
     * 逻辑删除，删除的通知会在七天后自动删除
     *
     * @param noticeId 通知ID
     * @return 默认响应类
     */
    @Override
    public Result<?> deleteMyNotice(Long noticeId) {
        deleteNoticeCache(noticeId);

        // Long receiveId = getCurrentUser().getId();
        Long receiveId = null;

        if (sysNoticeMapper.updateMyNoticeStatus(noticeId, receiveId, NoticeStatusEnum.DELETE.getCode()) <= 0) {
            log.error("删除我的通知失败，通知ID:{}", noticeId);
            throw new BusinessException("删除我的通知时发生异常");
        }

        log.info("删除我的通知成功，通知ID:{}", noticeId);
        return Result.ok("删除我的通知成功");
    }

    /**
     * 修改推送通知
     *
     * @param sysNoticeDTO 通知传输类
     * @return 默认响应类
     */
    @Override
    public Result<?> updateNotice(SysNoticeDTO sysNoticeDTO) {
        deleteNoticeCache(sysNoticeDTO.getId());

        SysNotice sysNotice = convertToSysNotice(sysNoticeDTO);

        try {
            sysNoticeMapper.updateNotice(sysNotice);
        } catch (Exception e) {
            log.error("修改推送通知失败，通知信息:{}", sysNotice);
            throw new BusinessException("修改推送通知时发生异常：", e);
        }


        log.info("修改推送通知成功，通知ID:{}", sysNotice.getId());
        return Result.ok("修改推送通知成功");
    }

    /**
     * 修改推送通知状态
     * 0正常 1撤回 2草稿 3删除
     *
     * @param noticeId 通知id
     * @param status   状态
     * @return 默认响应类
     */
    @Override
    public Result<?> updateNoticeStatus(Long noticeId, Integer status) {
        deleteNoticeCache(noticeId);

        if (sysNoticeMapper.updateNoticeStatus(noticeId, status) <= 0) {
            log.error("修改推送通知状态失败，通知id:{}", noticeId);
            throw new BusinessException("修改推送通知状态时发生异常");
        }


        log.info("修改推送通知状态成功，通知ID:{}", noticeId);
        return Result.ok("修改推送通知状态成功");
    }

    /**
     * 我的消息标记为已读
     *
     * @param noticeId 通知id
     * @return 默认响应类
     */
    @Override
    public Result<?> updateReadStatus(Long noticeId) {
        MyNotice myNotice = new MyNotice();
        myNotice.setNoticeId(noticeId);

        myNotice.setReadStatus(DefaultStatusEnum.ENABLED.getCode());

        // 设置接收者ID
        //myNotice.setReceiveId(getCurrentUser().getId());

        if (sysNoticeMapper.updateReadStatus(myNotice) <= 0) {
            log.error("标记通知已读失败，通知id:{}", noticeId);
            throw new BusinessException("标记通知已读时发送异常");
        }

        redisUtils.delete(getNoticeKey(noticeId));

        log.info("标记通知已读成功，通知ID:{}", noticeId);
        return Result.ok("标记通知已读成功");
    }

    /**
     * 获取推送通知
     *
     * @param noticeId 通知id
     * @return 通知响应类
     */
    @Override
    public Result<SysNoticeVO> getNoticeById(Long noticeId) {
        String noticeKey = getNoticeKey(noticeId);
        SysNoticeVO sysNoticeVO = getSysNoticeVOByCache(noticeKey);
        // 检查缓存是否命中，若命中直接返回
        if (ObjectUtils.isNotNull(sysNoticeVO)) {
            return Result.ok("获取推送通知成功", sysNoticeVO);
        }

        // 未命中则查询数据库
        SysNotice sysNotice = sysNoticeMapper.getNoticeById(noticeId);
        if (Objects.isNull(sysNotice)) {
            log.warn("未查找到相关通知，通知ID:{}", noticeId);
            throw new BusinessException("未查找到相关通知");
        }

        sysNoticeVO = convertToSysNoticeVO(sysNotice);

        // 保存缓存
        redisUtils.set(noticeKey, sysNoticeVO);

        log.info("获取推送通知成功");
        return Result.ok("获取推送通知成功", sysNoticeVO);
    }

    /**
     * 获取我的通知
     *
     * @param noticeId 通知id
     * @return 通知响应类
     */
    @Override
    public Result<SysNoticeVO> getMyNoticeById(Long noticeId) {
        String noticeKey = getNoticeKey(noticeId);
        SysNoticeVO sysNoticeVO = getSysNoticeVOByCache(noticeKey);
        // 检查缓存是否命中，若命中直接返回
        if (ObjectUtils.isNotNull(sysNoticeVO)) {
            return Result.ok("获取推送通知信息成功", sysNoticeVO);
        }

        //Long receiveId = getCurrentUser().getId();
        Long receiveId = null;

        // 未命中则查询数据库
        SysNotice sysNotice = sysNoticeMapper.getMyNoticeById(noticeId, receiveId);

        if (ObjectUtils.isNull(sysNotice)) {
            log.warn("未查找到相关消息，通知ID:{}", noticeId);
            throw new BusinessException("未查找到相关消息");
        }

        sysNoticeVO = convertToSysNoticeVO(sysNotice);

        // 保存缓存
        redisUtils.set(noticeKey, sysNoticeVO);
        log.info("获取我的消息成功");
        return Result.ok("获取我的消息成功", sysNoticeVO);
    }

    /**
     * 推送通知列表
     *
     * @param sysNoticeDTO 通知传输类
     * @return 通知响应类集合
     */
    @Override
    public Result<List<SysNoticeVO>> listNotice(SysNoticeDTO sysNoticeDTO) {
        SysNotice sysNotice = convertToSysNotice(sysNoticeDTO);
        String listNoticeKey = getNoticeListKey(sysNotice);
        List<SysNoticeVO> sysNoticeVOS = listSysNoticeVOByCache(listNoticeKey);
        if (!ObjectUtils.isNull(sysNoticeVOS)) {
            log.info("获取推送通知列表");
            return Result.ok("获取推送通知列表", sysNoticeVOS);
        }

        // 如果缓存中没有数据，则从数据库中查询
        //sysNotice.setSendId(getCurrentUser().getId());
        List<SysNotice> sysNotices = sysNoticeMapper.listNotice(sysNotice);
        if (sysNotices.isEmpty()) {
            log.warn("未查找到相关通知");
            return Result.error("未查找到相关通知");
        }

        sysNoticeVOS = convertToSysNoticeVOs(sysNotices);
        return Result.ok("查找通知列表成功", sysNoticeVOS);
    }

    /**
     * 我的通知列表
     *
     * @param sysNoticeDTO 通知传输类
     * @return 通知响应类集合
     */
    @Override
    public Result<List<SysNoticeVO>> listMyNotice(SysNoticeDTO sysNoticeDTO) {
        SysNotice sysNotice = convertToSysNotice(sysNoticeDTO);
        String listNoticeKey = getNoticeListKey(sysNotice);
        List<SysNoticeVO> sysNoticeVOS = listSysNoticeVOByCache(listNoticeKey);
        if (!ObjectUtils.isNull(sysNoticeVOS)) {
            log.info("查找我的消息列表成功");
            return Result.ok("查找我的消息列表成功", sysNoticeVOS);
        }

        // 如果缓存中没有数据，则从数据库中查询
        // 获取当前登录用户
        //Long receiveId = getCurrentUser().getId();
        Long receiveId = null;

        List<SysNotice> sysNotices = sysNoticeMapper.listMyNotice(sysNotice, receiveId);
        if (ObjectUtils.isEmpty(sysNotices)) {
            log.warn("未查找到相关通知");
            return Result.error("未查找到相关通知");
        }

        sysNoticeVOS = convertToSysNoticeVOs(sysNotices);
        // 将查询结果存入 Redis 缓存，并设置过期时间
        redisUtils.set(listNoticeKey, sysNoticeVOS, 60 * 60);
        return Result.ok("查找通知列表成功", sysNoticeVOS);
    }


    /**
     * 某通知的已读用户和未读用户列表
     *
     * @param noticeId 通知id
     * @return map集合，键为用户名，值为读取状态
     */
    @Override
    public Result<Map<String, Integer>> listNoticeReadIdsAndUnReadIds(Long noticeId) {
        try {
            // 获取通知的已读和未读用户 ID 映射
            Map<Long, Integer> readIdsAndUnReadIds = sysNoticeMapper.getNoticeReadIdsAndUnReadIds(noticeId);

            // 提取所有用户 ID
            Set<Long> userIds = readIdsAndUnReadIds.keySet();

            // 批量查询用户信息
            Map<Long, SysUser> userMap = userIds.stream()
                    .collect(Collectors.toMap(
                            id -> id,
                            id -> sysUserMapper.getUserById(id),
                            (existing, replacement) -> existing
                    ));

            // 处理已读和未读用户信息
            Map<String, Integer> readUsersAndUnReadUsers = readIdsAndUnReadIds.entrySet().stream()
                    .collect(Collectors.toMap(
                            entry -> userMap.get(entry.getKey()).getUsername(),
                            Map.Entry::getValue,
                            (existing, replacement) -> existing
                    ));

            return Result.ok("查找用户成功", readUsersAndUnReadUsers);
        } catch (Exception e) {
            // 异常处理
            throw new BusinessException("查找用户失败: " + e.getMessage());
        }
    }

    //------------------------ 工具方法  ------------------------
    // flag发送或撤回通知,0撤回 1发送
    private void NoticeToUsers(SysNotice sysNotice, Integer flag) {
        if (sysNotice.getStatus() == 2) {
            return;
        }
        Long noticeId = sysNotice.getId();
        String[] receiveIds = sysNotice.getReceiveIds().split(",");
        //0全部 1用户 2角色 3岗位 4部门 5公司
        switch (sysNotice.getReceiveType()) {
            case 0:
                sendOrRevokeToAllUser(noticeId, flag);
                break;
            case 1:
                sendOrRevokeToUsers(noticeId, receiveIds, flag);
                break;
            case 2:
                sendOrRevokeToUsersByRole(noticeId, receiveIds, flag);
                break;
            case 3:
                // TODO 岗位 获取某岗位下的所有用户
                break;
            case 4:
                // TODO 部门 获取某部门下的所有用户
                break;
            case 5:
                // TODO 公司
                break;
        }
    }

    private void sendOrRevokeToAllUser(Long noticeId, Integer flag) {
        List<SysUser> sysUsers = sysUserMapper.listUser(null);
        for (SysUser sysUser : sysUsers) {
            messageToUser(noticeId, flag, sysUser.getId());
        }
    }

    private void sendOrRevokeToUsers(Long noticeId, String[] receiveIds, Integer flag) {
        for (String receiveId : receiveIds) {
            messageToUser(noticeId, flag, Long.parseLong(receiveId));
        }
    }

    private void sendOrRevokeToUsersByRole(Long noticeId, String[] receiveIds, Integer flag) {
        for (String roleId : receiveIds) {
            long roleIdLong;
            roleIdLong = Long.parseLong(roleId);
            List<SysUser> sysUsers = sysRoleMapper.listUsersWithRole(roleIdLong);
            for (SysUser sysUser : sysUsers) {
                messageToUser(noticeId, flag, sysUser.getId());
            }
        }
    }

    private void messageToUser(Long noticeId, Integer flag, Long receiveId) {
        MyNotice myNotice = new MyNotice();
        myNotice.setNoticeId(noticeId);
        myNotice.setReadStatus(0);
        myNotice.setReceiveId(receiveId);
        if (flag == 1) {
            myNotice.setStatus(NoticeStatusEnum.NORMAL.getCode());
            producer.saveMyNotice(myNotice);
        } else {
            myNotice.setStatus(NoticeStatusEnum.REVOKED.getCode());
            sysNoticeMapper.revokeNoticeFromUser(myNotice);
        }
    }

    // 判断创建时间到现在是否满5分钟
    private boolean isFiveMinutesPassed(Date createDate) {
        if (createDate == null) {
            return false;
        }
        // 获取当前时间
        Date now = new Date();
        // 计算时间差（毫秒）
        long diff = now.getTime() - createDate.getTime();
        // 五分钟对应的毫秒数
        long fiveMinutesInMillis = 5 * 60 * 1000;
        // 判断时间差是否大于等于五分钟
        return diff >= fiveMinutesInMillis;
    }

    // 生成 Redis 缓存键
    private String getNoticeListKey(SysNotice sysNotice) {
        return RedisKeys.getSysNoticeListKey(sysNotice);
    }

    private String getNoticeKey(Long noticeId) {
        return RedisKeys.getSysNoticeKey(noticeId);
    }

    // 尝试从 Redis 中获取缓存数据
    private List<SysNoticeVO> listSysNoticeVOByCache(String key) {
        return redisUtils.getListByCache(key, SysNoticeVO.class);
    }

    private SysNoticeVO getSysNoticeVOByCache(String key) {
        return redisUtils.getByCache(key, SysNoticeVO.class);
    }

    //如果缓存存在则删除
    private void deleteNoticeCache(Long noticeId) {
        String key = getNoticeKey(noticeId);
        if (Objects.nonNull(redisUtils.get(key))) {
            redisUtils.delete(key);
        }
    }


    //TODO 获取当前登录用户
    private SecurityUser getCurrentUser() {
        SecurityUser securityUser = null;
        try {
            securityUser = (SecurityUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        } catch (Exception e) {
            log.error("获取当前登录用户时发生异常: {}", e.getMessage(), e);
            throw new BusinessException("获取当前登录用户失败");
        }
        if (securityUser == null) {
            log.error("获取当前登录用户失败");
            throw new BusinessException("获取当前登录用户失败");
        }
        return securityUser;
    }

    // 将 DTO 转换为实体
    private SysNotice convertToSysNotice(SysNoticeDTO sysNoticeDTO) {
        return ObjectConvertTool.originConvert(sysNoticeDTO, SysNotice.class);
    }

    // 将 实体 转换为 VO
    private List<SysNoticeVO> convertToSysNoticeVOs(List<SysNotice> sysNotices) {
        return ObjectConvertTool.originsConvert(sysNotices, SysNoticeVO.class);
    }

    private SysNoticeVO convertToSysNoticeVO(SysNotice sysNotice) {
        return ObjectConvertTool.originConvert(sysNotice, SysNoticeVO.class);
    }
}
